﻿using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;

namespace ayxjDllClass
{
    /// <summary>
    /// 适用于创芯科技，CANalyst-II（USB转CAN卡）
    /// </summary>
    public class CANFuncs
    {
        /// <summary>
        /// 设备类型，值为4
        /// USBCAN-2A、USBCAN-2C、CANalyst-II系列、MiniPCIe-CAN、汽车OBDII专用版
        /// </summary>
        public static UInt32 VCI_USBCAN2 = 4;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="DeviceType"></param>
        /// <param name="DeviceInd"></param>
        /// <param name="Reserved"></param>
        /// <returns></returns>
        /*------------兼容ZLG的函数描述---------------------------------*/
        [DllImport("controlcan.dll")]
        public static extern UInt32 VCI_OpenDevice(UInt32 DeviceType, UInt32 DeviceInd, UInt32 Reserved);

        [DllImport("controlcan.dll")]
        public static extern UInt32 VCI_CloseDevice(UInt32 DeviceType, UInt32 DeviceInd);

        [DllImport("controlcan.dll")]
        public static extern UInt32 VCI_InitCAN(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd, ref VCI_INIT_CONFIG pInitConfig);

        [DllImport("controlcan.dll")]
        public static extern UInt32 VCI_ReadBoardInfo(UInt32 DeviceType, UInt32 DeviceInd, ref VCI_BOARD_INFO pInfo);

        [DllImport("controlcan.dll")]
        public static extern UInt32 VCI_GetReceiveNum(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd);

        [DllImport("controlcan.dll")]
        public static extern UInt32 VCI_ClearBuffer(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd);

        [DllImport("controlcan.dll")]
        public static extern UInt32 VCI_StartCAN(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd);
        [DllImport("controlcan.dll")]
        public static extern UInt32 VCI_ResetCAN(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd);

        [DllImport("controlcan.dll")]
        public static extern UInt32 VCI_Transmit(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd, ref VCI_CAN_OBJ pSend, UInt32 Len);

        [DllImport("controlcan.dll")]
        public static extern UInt32 VCI_Receive(UInt32 DeviceType, UInt32 DeviceInd, UInt32 CANInd, ref VCI_CAN_OBJ pReceive, UInt32 Len, Int32 WaitTime);

        /*------------其他函数描述---------------------------------*/

        [DllImport("controlcan.dll")]
        public static extern UInt32 VCI_ConnectDevice(UInt32 DevType, UInt32 DevIndex);

        [DllImport("controlcan.dll")]
        public static extern UInt32 VCI_UsbDeviceReset(UInt32 DevType, UInt32 DevIndex, UInt32 Reserved);

        [DllImport("controlcan.dll")]
        public static extern UInt32 VCI_FindUsbDevice2(ref VCI_BOARD_INFO pInfo);
    }

    public class CanChannel
    {
        public bool IsOpen { get; set; } = false;
        /// <summary>
        /// 设备序号，从0开始
        /// </summary>
        public UInt32 DevIndex = 0;
        /// <summary>
        /// CAN通道号，从0开始
        /// </summary>
        public UInt32 canIndex = 0;

        Thread ReceThread = null;

        VCI_CAN_OBJ[] ReceiveList = new VCI_CAN_OBJ[2500];

        UInt32[] m_arrdevtype = new UInt32[20];

        /// <summary>
        /// 打开CAN卡设备，一个设备只能打开一次
        /// </summary>
        /// <param name="devIndex"></param>
        /// <param name="rlt">操作返回值，uint</param>
        /// <returns></returns>
        public bool Open(UInt32 devIndex, out uint rlt)
        {
            rlt = 1;
            if (!IsOpen)
            {
                DevIndex = devIndex;
                rlt = CANFuncs.VCI_OpenDevice(CANFuncs.VCI_USBCAN2, devIndex, 0);
                IsOpen = rlt == 1;
            }
            return IsOpen;
        }

        /// <summary>
        /// 以关闭设备
        /// </summary>
        /// <param name="rlt"></param>
        /// <returns></returns>
        public bool Close(out uint rlt) 
        {
            rlt = 1;
            if (IsOpen)
            {
                rlt = CANFuncs.VCI_CloseDevice(CANFuncs.VCI_USBCAN2, DevIndex);
                IsOpen = !(rlt == 1);
            }
            return rlt == 1;
        }

        /// <summary>
        /// 以初始化指定的CAN通道。有多个CAN通道时，需要多次调用
        /// </summary>
        /// <param name="initConfig"></param>
        /// <param name="canIndex"></param>
        /// <param name="rlt"></param>
        /// <returns></returns>
        public bool InitDev(ref VCI_INIT_CONFIG initConfig, uint canIndex, out uint rlt)
        {
            rlt = 0;
            if (IsOpen)
            {
                rlt = CANFuncs.VCI_InitCAN(CANFuncs.VCI_USBCAN2, DevIndex, canIndex, ref initConfig);
            }
            return rlt == 1;
        }

        /// <summary>
        /// 启动CAN卡的某一个CAN通道。有多个CAN通道时，需要多次调用
        /// </summary>
        /// <param name="canIndex"></param>
        /// <param name="rlt"></param>
        /// <returns></returns>
        public bool StartCan(uint canIndex, HandleMessage messageEvent, out uint rlt)
        {
            rlt = 0;
            if (IsOpen)
            {
                rlt = CANFuncs.VCI_StartCAN(CANFuncs.VCI_USBCAN2, DevIndex, canIndex);
                if (rlt == 1)
                {
                    if (HandleMessageEvent == null)
                        HandleMessageEvent = messageEvent;
                    if (ReceThread == null || ReceThread.ThreadState == ThreadState.Stopped || ReceThread.ThreadState == ThreadState.Aborted)
                    {
                        ReceThread = new Thread(new ThreadStart(ReceiveMessage)) { IsBackground = true };
                        ReceThread.Start();
                    }
                }
            }
            return rlt == 1;
        }

        /// <summary>
        /// 复位CAN，无需重新初始化，即可恢复CAN卡的正常状态
        /// </summary>
        /// <param name="canIndex"></param>
        /// <param name="rlt"></param>
        /// <returns></returns>
        public bool ResetCan(uint canIndex, HandleMessage messageEvent, out uint rlt)
        {
            rlt = 0;
            if (IsOpen)
            {
                rlt = CANFuncs.VCI_ResetCAN(CANFuncs.VCI_USBCAN2, DevIndex, canIndex);
                if (rlt == 1)
                {
                    if (HandleMessageEvent == null)
                        HandleMessageEvent = messageEvent;
                    if (ReceThread == null || ReceThread.ThreadState == ThreadState.Stopped || ReceThread.ThreadState == ThreadState.Aborted)
                    {
                        ReceThread = new Thread(new ThreadStart(ReceiveMessage)) { IsBackground = true };
                        ReceThread.Start();
                    }
                }
            }
            return rlt == 1;
        }

        /// <summary>
        /// 发送CAN消息
        /// </summary>
        /// <param name="canIndex"></param>
        /// <param name="pSend"></param>
        /// <returns>返回发送了几个数据帧</returns>
        public uint SendMessage(uint canIndex, VCI_CAN_OBJ pSend)
        {
            if (IsOpen)
            {
                return CANFuncs.VCI_Transmit(CANFuncs.VCI_USBCAN2, DevIndex, canIndex, ref pSend, 1);
            }
            return 0;
        }

        public delegate void HandleMessage(VCI_CAN_OBJ[] msgList, uint len);
        public event HandleMessage HandleMessageEvent = null;

        /// <summary>
        /// CAN数据帧接收处理方法
        /// </summary>
        public void ReceiveMessage()
        {
            while (IsOpen)
            {
                try
                {
                    uint len = CANFuncs.VCI_Receive(CANFuncs.VCI_USBCAN2, DevIndex, canIndex, ref ReceiveList[0], 2500, 100);
                    if (len > 0 && len != 0xffffffff)
                    {
                        if (HandleMessageEvent != null)
                            HandleMessageEvent(ReceiveList, len);
                    }
                    Task.Delay(100);
                }
                catch { }
            }
        }
    }

    #region 结构体声明

    /*------------兼容ZLG的数据类型---------------------------------*/

    //1.ZLGCAN系列接口卡信息的数据类型。
    //public struct VCI_BOARD_INFO 
    //{ 
    //    public UInt16 hw_Version;
    //    public UInt16 fw_Version;
    //    public UInt16 dr_Version;
    //    public UInt16 in_Version;
    //    public UInt16 irq_Num;
    //    public byte   can_Num;
    //    [MarshalAs(UnmanagedType.ByValArray, SizeConst=20)] public byte []str_Serial_Num;
    //    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 40)]
    //    public byte[] str_hw_Type;
    //    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    //    public byte[] Reserved;
    //}

    //以下为简易定义与调用方式，在项目属性->生成->勾选使用不安全代码即可
    unsafe public struct VCI_BOARD_INFO//使用不安全代码
    {
        public UInt16 hw_Version;
        public UInt16 fw_Version;
        public UInt16 dr_Version;
        public UInt16 in_Version;
        public UInt16 irq_Num;
        public byte can_Num;

        public fixed byte str_Serial_Num[20];
        public fixed byte str_hw_Type[40];
        public fixed byte Reserved[8];
    }


    /// <summary>
    /// 2.定义CAN信息帧的数据类型。
    /// </summary>
    unsafe public struct VCI_CAN_OBJ  //使用不安全代码
    {
        public uint ID;
        public uint TimeStamp;        //时间标识
        public byte TimeFlag;         //是否使用时间标识
        public byte SendType;         //发送标志。保留，未用
        public byte RemoteFlag;       //是否是远程帧
        public byte ExternFlag;       //是否是扩展帧
        public byte DataLen;          //数据长度
        public fixed byte Data[8];    //数据
        public fixed byte Reserved[3];//保留位
    }

    /// <summary>
    /// 3.定义初始化CAN的数据类型
    /// </summary>
    public struct VCI_INIT_CONFIG
    {
        /// <summary>
        /// 验收码 指定接收哪个ID的消息
        /// </summary>
        public UInt32 AccCode;
        /// <summary>
        /// 屏蔽码 4字节，对应位为0的是“有关位”，对应位为1的是“无关位”。屏蔽码推荐设置为0xFFFFFFFF，即全部接收。
        /// </summary>
        public UInt32 AccMask;
        /// <summary>
        /// 保留
        /// </summary>
        public UInt32 Reserved;
        /// <summary>
        /// 滤波方式：
        /// 0 或1接收所有帧
        /// 2 标准帧滤波
        /// 3 是扩展帧滤波
        /// </summary>
        public byte Filter;
        /// <summary>
        /// 波特率定时器 0（BTR0）
        /// </summary>
        public byte Timing0;
        /// <summary>
        /// 波特率定时器 1（BTR1）
        /// </summary>
        public byte Timing1;
        /// <summary>
        /// 模式。 
        /// 0 正常模式（相当于正常节点）；
        /// 1 只听模式（只接收，不影响总线）；
        /// 2 表示自发自收模式（环回模式）
        /// </summary>
        public byte Mode;
    }

    #region CAN波特率
    //1000Kbps    0x00    0x14
    //800Kbps     0x00    0x16
    //500Kbps     0x00    0x1C
    //250Kbps     0x01    0x1C
    //200Kbps     0x81    0xFA
    //125Kbps     0x03    0x1C
    //100Kbps     0x04    0x1C
    #endregion


    /*------------其他数据结构描述---------------------------------*/
    //4.USB-CAN总线适配器板卡信息的数据类型1，
    //该类型为VCI_FindUsbDevice函数的返回参数。
    public struct VCI_BOARD_INFO1
    {
        public UInt16 hw_Version;
        public UInt16 fw_Version;
        public UInt16 dr_Version;
        public UInt16 in_Version;
        public UInt16 irq_Num;
        /// <summary>
        /// 有几路CAN通道
        /// </summary>
        public byte can_Num;
        public byte Reserved;
        /// <summary>
        /// 板卡序列号
        /// </summary>
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] 
        public byte[] str_Serial_Num;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
        public byte[] str_hw_Type;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
        public byte[] str_Usb_Serial;
    }

    /*------------数据结构描述完成---------------------------------*/

    public struct CHGDESIPANDPORT
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
        public byte[] szpwd;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
        public byte[] szdesip;
        public Int32 desport;

        public void Init()
        {
            szpwd = new byte[10];
            szdesip = new byte[20];
        }
    }

    #endregion
}
